package com.emeraldparser.collect;

import java.util.Collection;
import java.util.Iterator;
import java.util.Set;

import com.google.common.base.Predicate;
import com.google.common.collect.ForwardingMultiset;
import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;
import com.google.common.collect.Sets;

public abstract class FilteredMultiset<E> extends ForwardingMultiset<E> {

	private final Predicate<E> elementPredicate = new Predicate<E>() {
		@Override
		public boolean apply(E element) {
			return FilteredMultiset.this.apply(element);
		}
	};

	private final Predicate<Entry<E>> entryPredicate = new Predicate<Entry<E>>() {
		@Override
		public boolean apply(Entry<E> entry) {
			return elementPredicate.apply(entry.getElement());
		}
	};

	protected abstract boolean apply(E element);

	protected boolean unsafeApply(Object unsafeElement) {
		@SuppressWarnings("unchecked")
		E element = (E) unsafeElement;
		
		try {
			return apply(element);
		} catch (ClassCastException e) {
			throw new IllegalArgumentException(e);
		}
	}

	protected boolean safeApply(Object unsafeElement) {
		try {
			return unsafeApply(unsafeElement);
		} catch (IllegalArgumentException e) {
			return false;
		}
	}

	protected E validate(E element) {
		if (!apply(element)) {
			throw new IllegalArgumentException();
		}
		
		return element;
	}

	@Override
	public int count(Object element) {
		if (safeApply(element)) {
			return delegate().count(element);
		} else {
			return 0;
		}
	}

	@Override
	public int add(E element, int occurrences) {
		return super.add(validate(element), occurrences);
	}

	@Override
	public int remove(Object element, int occurrences) {
		if (safeApply(element)) {
			return super.remove(element, occurrences);
		} else {
			return 0;
		}
	}

	@Override
	public Set<E> elementSet() {
		return Sets.filter(super.elementSet(), elementPredicate);
	}

	@Override
	public Set<Entry<E>> entrySet() {
		return Sets.filter(super.entrySet(), entryPredicate);
	}

	@Override
	public boolean equals(Object object) {
		// TODO Auto-generated method stub
		return super.equals(object);
	}

	@Override
	public int hashCode() {
		// TODO Auto-generated method stub
		return super.hashCode();
	}

	@Override
	public int setCount(E element, int count) {
		return super.setCount(validate(element), count);
	}

	@Override
	public boolean setCount(E element, int oldCount, int newCount) {
		return super.setCount(validate(element), oldCount, newCount);
	}

	@Override
	public Iterator<E> iterator() {
		return Iterators.filter(super.iterator(), elementPredicate);
	}

	@Override
	public int size() {
		return Iterables.size(this);
	}

	@Override
	public boolean removeAll(Collection<?> collection) {
		// TODO Auto-generated method stub
		return super.removeAll(collection);
	}

	@Override
	public boolean isEmpty() {
		return Iterables.isEmpty(this);
	}

	@Override
	public boolean contains(Object object) {
		return Iterables.contains(this, object);
	}

	@Override
	public boolean add(E element) {
		return super.add(validate(element));
	}

	@Override
	public boolean remove(Object object) {
		// TODO Auto-generated method stub
		return super.remove(object);
	}

	@Override
	public boolean containsAll(Collection<?> collection) {
		// TODO Auto-generated method stub
		return super.containsAll(collection);
	}

	@Override
	public boolean addAll(Collection<? extends E> collection) {
		// TODO Auto-generated method stub
		return super.addAll(collection);
	}

	@Override
	public boolean retainAll(Collection<?> collection) {
		// TODO Auto-generated method stub
		return super.retainAll(collection);
	}

	@Override
	public void clear() {
		// TODO Auto-generated method stub
		super.clear();
	}

}
